home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / Apps / EmacsTeX / Emacs-3.0.1 / Source / EtermView.m < prev    next >
Encoding:
Text File  |  1995-06-12  |  21.6 KB  |  881 lines

  1. /* The EtermView implementation.
  2.  
  3.    For legal stuff see the file COPYRIGHT.  */
  4.  
  5. #import <dpsclient/dpsclient.h>
  6. #import <mach/mach.h>
  7. #import <libc.h>
  8. #import <stdio.h>
  9. #import <stdlib.h>
  10. #import <sys/ioctl.h>
  11. #import <sys/param.h>
  12. #import <sys/time.h>
  13.  
  14. #import "EditListener.h"
  15. #import "EtermView.h"
  16. #import "EmacsApp.h"
  17. #import "etermSupport.h"
  18. #import "defaults.h"
  19. #import "display.h"
  20.  
  21. extern char **environ;
  22.  
  23. /* These are the window size limits imposed by GNUEmacs.  */
  24. #define MINWIDTH 10
  25. #define MINHEIGHT 5
  26. #define MAXWIDTH 300
  27. #define MAXHEIGHT 300
  28.  
  29. /* Function prototypes for display.pswm */
  30. void *new_display_rock (EtermView *theView);
  31. void input_from_emacs (int fd, void *theDisplay);
  32.  
  33. /* Function prototypes for keycode.c */
  34. void kc_init (void);
  35. int kc_keyforcode (int code, int flags);
  36.  
  37. /* write(2), except it will keep retrying upon partial success.  */
  38. static int
  39. write_safely (int fd, char *buf, int n)
  40. {
  41.   int i, start = 0;
  42.  
  43.   while (start < n)
  44.     {
  45.       i = write (fd, buf + start, n - start);
  46.       if (i == -1)
  47.     return i; /* Some error */
  48.       start += i;
  49.     }
  50.   return n;
  51. }
  52.  
  53. /* Handle a connection to the event server socket */
  54. static void
  55. connection_from_emacs (int fd, void *rock)
  56. {
  57.   EtermView *view = (EtermView *) rock;
  58.   int newfd = accept_server_connection (fd);
  59.  
  60.   if (newfd == -1)
  61.     return;
  62.  
  63.   if ([view eventChannel])
  64.     close (newfd);
  65.   else
  66.     [view setEventChannel: fdopen (newfd, "w")];
  67. }
  68.  
  69. @implementation EtermView
  70.  
  71. + initialize
  72. {
  73.   static NXDefaultsVector etermDefaults =
  74.     {
  75.       {"NXAutoLaunch", "NO" },
  76.       {"NXFixedPitchFontSpacing", "0.95" },
  77.       {"HideOnAutoLaunch", "NO" },
  78.       {"EmacsPath", DEFAULT_EMACS_PATH},
  79.       {"LispPath", DEFAULT_LISP_PATH},
  80.       {NULL},
  81.     };
  82.   const char *sTypes[2], *rTypes[2];
  83.  
  84.   sTypes[0] = NXAsciiPboardType;
  85.   sTypes[1] = NULL;
  86.   rTypes[0] = NXAsciiPboardType;
  87.   rTypes[1] = NULL;
  88.  
  89.   NXRegisterDefaults ([NXApp appName], etermDefaults);
  90.   [NXApp registerServicesMenuSendTypes: sTypes andReturnTypes: rTypes];
  91.   kc_init ();
  92.  
  93.   return self;
  94. } /* +initialize */
  95.  
  96. -initFrame: (const NXRect *) newFrame
  97. {
  98.   self = [super initFrame: newFrame];
  99.   [self setFlipped: YES];
  100. #if 0
  101.   [self notifyAncestorWhenFrameChanged: YES];
  102. #endif
  103.  
  104.   spacing = atof (NXGetDefaultValue ([NXApp appName],
  105.                      "NXFixedPitchFontSpacing"));
  106.   /* Check for sane values of spacing.  */
  107.   if (spacing < 0.9)
  108.     spacing = 0.9;
  109.   else if (spacing > 2.0)
  110.     spacing = 2.0;
  111.   [self setFont: [self font]];
  112.   [[NXApp fontManager] setSelFont: [self font] isMultiple: NO];
  113.   return self;
  114. } /* -initFrame: */
  115.  
  116. -(float) spacing
  117. {
  118.   return spacing;
  119. }
  120.  
  121. -setSpacing: (float) newSpacing
  122. {
  123.   char val[20];
  124.  
  125.   spacing = newSpacing;
  126.   [self setFont: [self font]];
  127.   sprintf (val, "%f", newSpacing);
  128.   NXWriteDefault ([NXApp appName], "NXFixedPitchFontSpacing", val);
  129.   return self;
  130. }
  131.  
  132. -font
  133. {
  134.   return [Font userFixedPitchFontOfSize: (float) 0 matrix: NX_FLIPPEDMATRIX];
  135. } /* -font */
  136.  
  137. -setFont: newFont
  138. {
  139.   NXCoord ascender, descender, lineHeight;
  140.   NXRect frameRect, contentRect;
  141.   Font *screenfont;
  142.   int leading;
  143.  
  144.   /* NXTextFontInfo is broken for screen fonts, so call it before converting
  145.      the font to a screenfont.  */
  146.   NXTextFontInfo (newFont , &ascender, &descender, &lineHeight);
  147.   screenfont = [newFont screenFont];
  148.   if (screenfont)
  149.     newFont = screenfont;
  150.  
  151.   leading = (spacing - 1.0) * lineHeight;
  152.   fontHeight = (int) (lineHeight * spacing + leading);
  153.   fontWidth = (float) ([newFont getWidthOf : "  "]
  154.                - [newFont getWidthOf : " "] + 0.5);
  155.   fontDescender = (int) (descender + leading);
  156.  
  157.   [newFont set];
  158.   if (screenfont)
  159.     set_font ();
  160.   else
  161.     fix_font (fontWidth);
  162.   displayFont = newFont;
  163.  
  164.   /* Snap the window size to a 1 x 1 character grid.  */
  165.   [window getFrame: &frameRect];
  166.   [self windowWillResize: self toSize: &frameRect.size];
  167.   [Window getContentRect: &contentRect forFrameRect: &frameRect
  168.           style: [window style]];
  169.   [window sizeWindow: contentRect.size.width : contentRect.size.height];
  170.   [self windowDidResize: self];
  171.   [self display];
  172.  
  173.   return self;
  174. } /* -setFont: */
  175.  
  176. /* Sent by fontManager.  */
  177. -changeFont: sender
  178. {
  179.   id selectedFont;
  180.  
  181.   /* Get the font selected in the FontPanel.  */
  182.   selectedFont = [[NXApp fontManager]
  183.           convertFont: [[NXApp fontManager] selFont]];
  184.   [self setFont: selectedFont];
  185.   [Font setUserFixedPitchFont: selectedFont];
  186.   return self;
  187. } /* -changeFont: */
  188.  
  189. -(FILE *) eventChannel;
  190. {
  191.   return eventChannel;
  192. } /* -eventChannel */
  193.  
  194. -setEventChannel: (FILE *) fp;
  195. {
  196.   eventChannel = fp;
  197.   return self;
  198. } /* -setEventChannel: */
  199.  
  200. /* Constrain window so that the content view has an integral width in terms
  201.    of characters.  XXX this method needs some cleaning up to stop the window
  202.    from flashing while resizing.  */
  203. -windowWillResize: sender toSize: (NXSize *) frameSize;
  204. {
  205.   static int oldw = 0, oldh = 0;
  206.   int neww, newh;
  207.   NXRect frameRect, contentRect;
  208.   int newwidth, newheight;
  209.   int style = [window style];
  210.  
  211.   NXSetRect (&frameRect, 0.0, 0.0, frameSize->width, frameSize->height);
  212.   [Window getContentRect: &contentRect forFrameRect: &frameRect style: style];
  213.  
  214.   newwidth = (int) (contentRect.size.width + fontWidth / 2 - BORDER_WIDTH * 2);
  215.   newheight = (int) (contentRect.size.height
  216.              + fontHeight / 2 - BORDER_WIDTH * 2);
  217.  
  218.   newwidth -= newwidth % fontWidth;
  219.   newheight -= newheight % fontHeight;
  220.  
  221.   if (newwidth < MINWIDTH * fontWidth)
  222.     newwidth = MINWIDTH * fontWidth;
  223.   if (newwidth > MAXWIDTH * fontWidth)
  224.     newwidth = MAXWIDTH * fontWidth;
  225.  
  226.   if (newheight < MINHEIGHT * fontHeight)
  227.     newheight = MINHEIGHT * fontHeight;
  228.   if (newheight > MAXHEIGHT * fontHeight)
  229.     newheight = MAXHEIGHT * fontHeight;
  230.  
  231.   contentRect.size.width = (NXCoord) newwidth + BORDER_WIDTH * 2;
  232.   contentRect.size.height = (NXCoord) newheight + BORDER_WIDTH * 2;
  233.   [Window getFrameRect:&frameRect forContentRect:&contentRect style:style];
  234.  
  235.   frameSize->width = frameRect.size.width;
  236.   frameSize->height = frameRect.size.height;
  237.  
  238.   newh = newheight / fontHeight;
  239.   neww = newwidth / fontWidth;
  240.   if (newh != oldh || neww != oldw)
  241.     {
  242.       [self showTitle: newh : neww];
  243.       oldh = newh;
  244.       oldw = neww;
  245.     }
  246.   return self;
  247. } /* -windowWillResize:toSize: */
  248.  
  249. /* Convert size to number of characters in content view.  Inform child emacs
  250.    process of our new size.  */
  251. -windowDidResize: sender
  252. {
  253.   NXRect frameRect, contentRect;
  254.   int style = [window style];
  255.   struct winsize winsize;
  256.  
  257.   [window getFrame: &frameRect];
  258.   [Window getContentRect: &contentRect forFrameRect: &frameRect style: style];
  259.  
  260.   lines = (int) ((contentRect.size.height - BORDER_WIDTH * 2) / fontHeight);
  261.   cols =  (int) ((contentRect.size.width - BORDER_WIDTH * 2) / fontWidth);
  262.   [self showTitle: lines : cols];
  263.  
  264.   /* emacs doesn't do anything with a TIOCCSWINSZ if LINES nor COLS change,
  265.      so send a redraw screen in that case.  */
  266.   winsize.ws_row = lines;
  267.   winsize.ws_col = cols;
  268.   winsize.ws_xpixel = (int) contentRect.size.width;
  269.   winsize.ws_ypixel = (int) contentRect.size.height;
  270.   ioctl (masterChannel, TIOCSWINSZ, &winsize);
  271.   if (eventChannel)
  272.     fprintf (eventChannel, "(redraw-display)\n");
  273.   return self;
  274. } /* -windowDidResize: */
  275.  
  276. /* Start up the child emacs process, perhaps on a file.  */
  277. -startEmacs
  278. {
  279.   int master, slave, ptynumber, portno, i, n;
  280.   char **args, **env;
  281.   char *path;
  282.   const char *lisp;
  283.  
  284.   /* Grab a pty/tty pair */
  285.   create_channel (&master, &slave, &ptynumber);
  286.   masterChannel = master;
  287.  
  288.   /* Create the server socket */
  289.   eventServerSocket = create_server_socket (&portno);
  290.   DPSAddFD (eventServerSocket, connection_from_emacs,
  291.         (void *) self, NX_RUNMODALTHRESHOLD);
  292.  
  293.   /* Frob the environment */
  294.   env = patch_env (environ, portno);
  295.  
  296.   /* Make sure the size is set correctly */
  297.   [self windowDidResize: self];
  298.  
  299.   args = calloc (startNum + 6, sizeof (char *));
  300.   args[0] = "emacs";
  301.  
  302.   path = malloc (MAXPATHLEN + 1);
  303.   lisp = NXGetDefaultValue ([NXApp appName], "LispPath");
  304.   args[1] = "-l";
  305.   args[3] = "-f";
  306.   args[4] = "start-event-server";
  307.   n = 5;
  308.   if (strrchr (lisp, '/'))
  309.     args[2] = (char *)lisp;
  310.   else
  311.     {
  312.       if ([[NXBundle mainBundle] getPath: path forResource: lisp ofType: NULL])
  313.     args[2] = path;
  314.       else
  315.     n = 1;
  316.     }
  317.  
  318.   for (i=0 ; i<startNum ; i++) {
  319.       args[n++] = startFiles[i];
  320.   }
  321.  
  322.   /* Fork off the Emacs */
  323.   fork_shell ((char *)NXGetDefaultValue ([NXApp appName], "EmacsPath"),
  324.           args, env, slave);
  325.   free (path);
  326.   free (args);
  327.   close (slave);
  328.  
  329.   rock = new_display_rock(self);
  330.   DPSAddFD (master, input_from_emacs, rock, NX_RUNMODALTHRESHOLD);
  331.   [window display];
  332.   return self;
  333. } /* -startEmacs: */
  334.  
  335. -(BOOL) acceptsFirstResponder
  336. {
  337.   return YES;
  338. } /* -acceptsFirstResponder */
  339.  
  340. /* Keyboard input is given to the child emacs process.  XXX this method needs some cleeaning up.  */
  341. -keyDown: (NXEvent *) theEvent;
  342. {
  343.   NXEvent eventbuf;
  344.   char buf[1024];
  345.   int i = 0;
  346.   int code;
  347.  
  348.   if (theEvent->flags & NX_NUMERICPADMASK
  349.       && theEvent->data.key.charCode >= 0xac
  350.       && theEvent->data.key.charCode <= 0xaf)
  351.     {
  352.       /* Arrow keys (left, up, right, down) */
  353.       if (theEvent->flags & NX_SHIFTMASK)
  354.     buf[i++] = "\302\326\306\026"[theEvent->data.key.charCode - 0xac];
  355.       else if (theEvent->flags & NX_CONTROLMASK)
  356.     buf[i++] = "\001\274\005\276"[theEvent->data.key.charCode - 0xac];
  357.       else if (theEvent->flags & NX_ALTERNATEMASK)
  358.     buf[i++] = "\202\220\206\216"[theEvent->data.key.charCode - 0xac];
  359.       else
  360.     buf[i++] = "\002\020\006\016"[theEvent->data.key.charCode - 0xac];
  361.     }
  362.   else if (theEvent->flags & NX_NUMERICPADMASK
  363.        && theEvent->data.key.charCode == 0x03)
  364.     {
  365.       /* Enter key */
  366.       buf[i++] = '\r';
  367.     }
  368.   else if (theEvent->flags & NX_ALTERNATEMASK)
  369.     {
  370.       /* Handle ALT key as a META key */
  371.       code = kc_keyforcode(theEvent->data.key.keyCode, theEvent->flags);
  372.       if (code != -1) {
  373.     buf[i++] = code | 0x80;
  374.       }
  375.     }
  376.   else buf[i++] = theEvent->data.key.charCode;
  377.  
  378.   /* Grab as many keypressed events as we can from a modal event loop */
  379.   while (i < sizeof (buf)
  380.      && (theEvent = [NXApp peekNextEvent: NX_ALLEVENTS into: &eventbuf])
  381.      && theEvent->type == NX_KEYDOWN
  382.      && !(theEvent->flags & NX_COMMANDMASK))
  383.     {
  384.       theEvent = [NXApp getNextEvent: NX_ALLEVENTS];
  385.  
  386.       if (theEvent->flags & NX_NUMERICPADMASK
  387.       && theEvent->data.key.charCode >= 0xac
  388.       && theEvent->data.key.charCode <= 0xaf)
  389.     {
  390.       /* Arrow keys (left, up, right, down) */
  391.       if (theEvent->flags & NX_SHIFTMASK)
  392.         buf[i++] = "\302\326\306\026"[theEvent->data.key.charCode - 0xac];
  393.       else if (theEvent->flags & NX_CONTROLMASK)
  394.         buf[i++] = "\001\274\005\276"[theEvent->data.key.charCode - 0xac];
  395.       else if (theEvent->flags & NX_ALTERNATEMASK)
  396.         buf[i++] = "\202\220\206\216"[theEvent->data.key.charCode - 0xac];
  397.       else
  398.         buf[i++] = "\002\020\006\016"[theEvent->data.key.charCode - 0xac];
  399.     }
  400.       else if (theEvent->flags & NX_NUMERICPADMASK
  401.            && theEvent->data.key.charCode == 0x03)
  402.     {
  403.       /* Enter key */
  404.       buf[i++] = '\r';
  405.     }
  406.       else if (theEvent->flags & NX_ALTERNATEMASK)
  407.     {
  408.       /* Handle ALT key as a META key */
  409.       code = kc_keyforcode (theEvent->data.key.keyCode, theEvent->flags);
  410.       if (code != -1)
  411.         buf[i++] = code | 0x80;
  412.     }
  413.       else buf[i++] = theEvent->data.key.charCode;
  414.     }
  415.  
  416.   write_safely (masterChannel, buf, i);
  417.   obscure_cursor ();
  418.  
  419.   return self;
  420. } /* -keyDown: */
  421.  
  422. /* Mouse input is converted to a character position and given to the child
  423.    emacs process, but only if the emacs has opened an event port.  */
  424. -mouseEvent: (NXEvent *) theEvent : (int) button;
  425. {
  426.   int x, y;
  427.   char buf[35];
  428.  
  429.   if (!eventChannel)
  430.     return self;
  431.  
  432.   [self convertPoint: &theEvent->location fromView:nil];
  433.   x = (int) ((theEvent->location.x - BORDER_WIDTH) / fontWidth);
  434.   y = (int) ((theEvent->location.y - BORDER_WIDTH) / fontHeight);
  435.  
  436.   if (x < 0)
  437.     x = 0;
  438.   if (y < 0)
  439.     y = 0;
  440.   if (x >= cols)
  441.     x = cols - 1;
  442.   if (y >= lines)
  443.     y = lines - 1;
  444.  
  445.   if (oldx != x || oldy != y) {
  446.  
  447.       sprintf (buf, "\030%c(%d %d %d 1)\r", 0,
  448.            (button + ((theEvent->flags & NX_SHIFTMASK) ? 8 : 0)
  449.         + ((theEvent->flags & NX_CONTROLMASK) ? 16 : 0)
  450.         + ((theEvent->flags & NX_ALTERNATEMASK) ? 32 : 0)), x, y);
  451.       write_safely (masterChannel, buf, 2+strlen (buf+2));
  452.  
  453.       oldx = x;
  454.       oldy = y;
  455.     }
  456.  
  457.   return self;
  458. } /* -mouseEvent:: */
  459.  
  460.  
  461. -mouseDown: (NXEvent *) theEvent;
  462. {
  463.   [self mouseEvent: theEvent: 1];
  464.   oldmask = [window addToEventMask:NX_LMOUSEDRAGGEDMASK];
  465.   return self;
  466. } /* -mouseDown: */
  467.  
  468. -mouseUp: (NXEvent *) theEvent;
  469. {
  470.   oldmask = [window removeFromEventMask:NX_LMOUSEDRAGGEDMASK];
  471.   [self mouseEvent: theEvent: 1 + 128];
  472.   return self;
  473. } /* -mouseUp */
  474.  
  475. -mouseDragged:  (NXEvent *) theEvent;
  476. {
  477.   NXEvent    *foundEvent = theEvent, *searchEvent;
  478.   int        filterCount = 0;
  479.  
  480.   while ((++filterCount < 10) &&
  481.      ((searchEvent = [NXApp getNextEvent:NX_LMOUSEDRAGGEDMASK waitFor:0.034
  482.               threshold:NX_MODALRESPTHRESHOLD]) != NULL))
  483.       foundEvent = searchEvent;
  484.  
  485.   [self mouseEvent: foundEvent: 1 + 128];
  486.   return self;
  487. } /* -mouseDragged */
  488.  
  489. -rightMouseDown: (NXEvent *) theEvent;
  490. {
  491.   [self mouseEvent: theEvent: 4];
  492.   return self;
  493. } /* rightMouseDown: */
  494.  
  495. -rightMouseUp: (NXEvent *) theEvent;
  496. {
  497.   [self mouseEvent: theEvent: 4 + 128];
  498.   return self;
  499. } /* rightMouseUp: */
  500.  
  501. -(BOOL) emacsOn
  502. {
  503.   if (eventChannel) {
  504.     if (startNum > 0) {
  505.       int index;
  506.  
  507.       /* Now that we have a connection established, make sure all the open
  508.          requests that have line numbers are pointed at the right place.   */
  509.       for (index = 0 ; index < startNum ; index++) {
  510.     if (startLines[index] >= 0)
  511.       [self openFile:startFiles[index] onHost:"" 
  512.         atTrueLine:startLines[index]];
  513.     else
  514.       [self newFile:startFiles[index]];
  515.       }
  516.       startNum = 0;
  517.     }
  518.     return YES;
  519.   }
  520.   else
  521.     return NO;
  522. } /* -emacsOn */
  523.  
  524. -(BOOL) newFile: (const char *) path
  525. {
  526.   if (!eventChannel)
  527.     return [self addFile:path atLine:-1];
  528.  
  529.   fprintf (eventChannel, "(find-file \"%s\")\n", path);
  530.   fflush (eventChannel);
  531.   return YES;
  532. } /* -newFile: */
  533.     
  534. /* Quit, cut, copy, paste, and undo messages are sent to the child's event
  535.    port.  */
  536. -quitEmacs
  537. {
  538.   if (!eventChannel)
  539.     return nil;
  540.   fprintf (eventChannel, "(event-quit)\n");
  541.   fflush (eventChannel);
  542.   return self;
  543. } /* -quitEmacs */
  544.  
  545. -pasteboard
  546. {
  547.   if (!currentPasteboard)
  548.     currentPasteboard = [self appPasteboard];
  549.   return currentPasteboard;
  550. } /* -pasteboard */
  551.  
  552. -appPasteboard
  553. {
  554.   if (!mainPasteboard)
  555.     mainPasteboard = [Pasteboard new];
  556.   return mainPasteboard;
  557. } /* -appPasteboard */
  558.  
  559. -setPasteboard: pboard
  560. {
  561.   if (pboard != nil)
  562.     currentPasteboard = pboard;
  563.   return self;
  564. } /* -setPasteboard: */
  565.  
  566. -(BOOL) sendEmacsEvent: (char *) theEvent
  567. {
  568.   if (!eventChannel)
  569.     return NO;
  570.   fprintf (eventChannel, "%s\n", theEvent);
  571.   fflush (eventChannel);
  572.   return YES;
  573. } /* -(BOOL) sendEmacsEvent: */
  574.  
  575. -pasteboardWritten
  576. {
  577.   pasteboardWaiting = NO;
  578.   return self;
  579. }
  580.  
  581. -(BOOL)pasteboardWaiting
  582. {
  583.   return pasteboardWaiting;
  584. }
  585.  
  586.  
  587. -undo: sender;
  588. {
  589.   [self sendEmacsEvent: "(undo)"];
  590.   return self;
  591. } /* -undo: */
  592.  
  593. -save: sender;
  594. {
  595.   [self sendEmacsEvent: "(save-buffer)"];
  596.   return self;
  597. } /* -save: */
  598.  
  599. -saveAll: sender;
  600. {
  601.   [self sendEmacsEvent: "(save-some-buffers t)"];
  602.   return self;
  603. } /* -saveAll: */
  604.  
  605. -cut: sender;
  606. {
  607.   [self cutTo: [self appPasteboard]];
  608.   return self;
  609. } /* -cut: */
  610.  
  611. -copy: sender;
  612. {
  613.   [self copyTo: [self appPasteboard]];
  614.   return self;
  615. } /* -copy: */
  616.  
  617. -paste: sender;
  618. {
  619.   [self pasteFrom: [self appPasteboard]];
  620.   return self;
  621. } /* -paste: */
  622.  
  623. -open: sender;
  624. {
  625.   int i;
  626.   char *path;
  627.   const char *directory;
  628.   const char *const *fileNames;
  629.  
  630.   if (!eventChannel)
  631.     return self;
  632.   if (!openPanel)
  633.     openPanel = [OpenPanel new];
  634.   [openPanel allowMultipleFiles: YES];
  635.   [openPanel chooseDirectories: NO];
  636.   if ([openPanel runModal])
  637.     {
  638.       directory = [openPanel directory];
  639.       fileNames = [openPanel filenames];
  640.       if (fileNames)
  641.     {
  642.       for (i = 0; fileNames[i]; i++)
  643.         {
  644.           path = malloc (2 + strlen (directory) + strlen (fileNames[i]));
  645.           strcat (strcat (strcpy (path, directory), "/"), fileNames[i]);
  646.           fprintf (eventChannel, "(find-file \"%s\")\n", path);
  647.           fflush (eventChannel);
  648.           free (path);
  649.         }
  650.     }
  651.     }
  652.   return self;
  653. } /* -open: */
  654.  
  655. -saveAs: sender;
  656. {
  657.   const char *path;
  658.  
  659.   if (!eventChannel)
  660.     return self;
  661.   if (!savePanel)
  662.     savePanel = [SavePanel new];
  663.   if ([savePanel runModal])
  664.     {
  665.       path = [savePanel filename];
  666.       if (path)
  667.     {
  668.       fprintf (eventChannel, "(write-file \"%s\")\n", path);
  669.       fflush (eventChannel);
  670.     }
  671.     }
  672.   return self;
  673. } /* -saveAs: */
  674.  
  675. -(BOOL) cutTo: pasteboard
  676. {
  677.   return [[self setPasteboard: pasteboard] sendEmacsEvent: "(event-cut)"];
  678. } /* -(BOOL) cutTo: */
  679.  
  680. -(BOOL) copyTo: pasteboard
  681. {
  682.   return [[self setPasteboard: pasteboard] sendEmacsEvent: "(event-copy)"];
  683. } /* -(BOOL) copyTo: */
  684.  
  685. -(BOOL) pasteFrom: pasteboard
  686. {
  687.   const NXAtom *types, *p;
  688.   char *data;
  689.   int len;
  690.  
  691.   if (!eventChannel || !pasteboard)
  692.     return NO;
  693.   types = [pasteboard types];
  694.   for (p = types; *p && strcmp (*p, NXAsciiPboardType); p++);
  695.  
  696.   if (*p && [pasteboard readType: NXAsciiPboardType data: &data length: &len]
  697.       && len)
  698.     {
  699.       int c;
  700.       char *tosend = data;
  701.  
  702.       fputs("(event-paste \"", eventChannel);
  703.  
  704.       /* Write out string, quoting quote, backslash, and newline */
  705.       for (tosend = data; len--; tosend++)
  706.     {
  707.       switch (c = *tosend)
  708.         {
  709.         case '\n':
  710.           fputs ("\\n", eventChannel);
  711.           break;
  712.         case '\\':
  713.         case '\"':
  714.           putc('\\', eventChannel);
  715.           /* FALL THROUGH */
  716.         default:
  717.           putc (c, eventChannel);
  718.           break;
  719.         }
  720.     }
  721.       fputs ("\")\n", eventChannel);
  722.       fflush (eventChannel);
  723.       vm_deallocate (task_self (), (vm_address_t) data, len);
  724.     }
  725.   return YES;
  726. } /* -(BOOL) pasteFrom: */
  727.  
  728. -getDimensions: (int *) linesPtr : (int *) colsPtr
  729. {
  730.   *linesPtr = lines;
  731.   *colsPtr = cols;
  732.   return self;
  733. } /* -getDimensions:: */
  734.  
  735. -(Font *) getDisplayFont:
  736.   (int *) heightPtr :
  737.   (int *) widthPtr :
  738.   (int *) descenderPtr
  739. {
  740.   *heightPtr = fontHeight;
  741.   *widthPtr = fontWidth;
  742.   *descenderPtr = fontDescender;
  743.   return displayFont;
  744. } /* -getDisplayFont::: */
  745.  
  746. -showTitle: (int) newLines : (int) newColumns
  747. {
  748.   char *newtitle;
  749.  
  750.   if (!titleprefix)
  751.     return self;
  752.   newtitle = malloc (22 + strlen (titleprefix));
  753.   sprintf (newtitle, "%s (%dx%d)", titleprefix, newColumns, newLines);
  754.   [(Window *) window setTitle: newtitle];
  755.   free (newtitle);
  756.   return self;
  757. } /* -showTitle:: */
  758.  
  759. -setTitle: (char *) title
  760. {
  761.   titleprefix = title;
  762.   [self showTitle: lines : cols];
  763.   return self;
  764. } /* -setTitle: */
  765.  
  766. -validRequestorForSendType: (NXAtom) typeSent
  767.  andReturnType: (NXAtom) typeReturned
  768. {
  769.   /* Check to make sure that the types are ones that we can handle.  */
  770.   if (eventChannel && (typeSent == NXAsciiPboardType || typeSent == NULL)
  771.       && (typeReturned == NXAsciiPboardType || typeReturned == NULL))
  772.     return self;
  773.   /* Otherwise, return the default.  */
  774.   return [super validRequestorForSendType: typeSent
  775.                 andReturnType: typeReturned];
  776. } /* -validRequestorForSendType:andReturnType: */
  777.  
  778. -(BOOL) writeSelectionToPasteboard: pboard types: (NXAtom *) types
  779. {
  780.   const NXAtom    *p;
  781.   BOOL            rightType = NO;
  782.  
  783.   if (eventChannel && pboard && rock)
  784.     {
  785.       p = types;
  786.       while (*p) {
  787.     if (!strcmp(*p, NXAsciiPboardType)) {
  788.       rightType = YES;
  789.       break;
  790.     }
  791.     p++;
  792.       }
  793.  
  794.       /* Ok, so this is slightly sick.  We loop until emacs receives and
  795.          processes the event.  Then we know we've completely dealt with the
  796.          pasteboard.  We can't return until something has been pasted to it.
  797.          Or the application will probably end up choking.  We put in a
  798.          timeout to prevent hanging.                       */
  799.       if (rightType) {
  800.     struct timeval    theTime;
  801.     struct timezone    dummy;
  802.     long        curTime, elapsedTime;
  803.  
  804.     gettimeofday(&theTime, &dummy);
  805.     curTime = theTime.tv_sec + theTime.tv_usec / 1000000;
  806.     elapsedTime = 0;
  807.     pasteboardWaiting = YES;
  808.     if (![self copyTo: pboard])
  809.       return NO;
  810.     while (pasteboardWaiting &&
  811.            (elapsedTime < 5)) {
  812.       input_from_emacs(masterChannel, rock);
  813.       gettimeofday(&theTime, &dummy);
  814.       elapsedTime = (theTime.tv_sec + theTime.tv_usec / 1000000) - curTime;
  815.     }
  816.     if (pasteboardWaiting) {
  817.       NXBeep();
  818.       NXBeep();
  819.       return NO;
  820.     }
  821.     return YES;
  822.       }
  823.     }
  824.   return NO;
  825. } /* -writeSelectionToPasteboard:types: */
  826.  
  827. -readSelectionFromPasteboard: pboard
  828. {
  829.   [self sendEmacsEvent: "(call-interactively 'kill-region)"];
  830.   [self pasteFrom: pboard];
  831.   return self;
  832. } /* -readSelectionFromPasteboard: */
  833.  
  834.  
  835.   /* Opening up a file remotely and showing the right line number.       */
  836. -(int)openFile : (char *) fileName
  837.     onHost : (char *) hostName
  838.     atTrueLine : (int) line
  839. {
  840.   if (!eventChannel)
  841.     return [self addFile:fileName atLine:line];
  842.  
  843.   fprintf(eventChannel, "(find-file \"%s\")\n", fileName);
  844.   fprintf(eventChannel, "(goto-line %d)\n", line);
  845.   fprintf(eventChannel, "(if (not overlay-arrow-position)");
  846.   fprintf(eventChannel, "    (progn (setq overlay-arrow-position (point-marker)) ");
  847.   fprintf(eventChannel, "           (setq overlay-arrow-string \"=>\")))\n");
  848.   fprintf(eventChannel, "(set-marker overlay-arrow-position ");
  849.   fprintf(eventChannel, "            (- (point) (current-column)))\n");
  850.   fflush(eventChannel);
  851.   return YES;
  852. }
  853.  
  854. -(int)openFile : (char *) fileName
  855.     onHost : (char *) hostName
  856.     fromTrueLine : (int) line1
  857.     to : (int) line2
  858. {
  859.   return [self openFile:fileName onHost:hostName atTrueLine:line1];
  860. }
  861.  
  862. - (BOOL)addFile:(const char *)path atLine:(int)line
  863. {
  864.   if (startFiles == NULL) {
  865.     startFiles = (char **)malloc((startNum + 1) * sizeof(char *));
  866.     startLines = (int *)malloc((startNum + 1) * sizeof(int));
  867.   }
  868.   else {
  869.     startFiles = (char **)realloc(startFiles, (startNum+1) * sizeof (char *));
  870.     startLines = (int *)realloc(startLines, (startNum+1) * sizeof (int));
  871.   }
  872.   if ((startFiles == NULL) || (startLines == NULL)) return NO;
  873.  
  874.   startLines[startNum] = line;
  875.   startFiles[startNum] = strcpy (malloc (1 + strlen (path)), path);
  876.   startNum++;
  877.   return YES;
  878. }
  879.  
  880. @end
  881.